#pragma once
#include "../RefCountedObject.h"

namespace SevenZip
{

struct CSevenZipLibrary : CRefCountedObject<CSevenZipLibrary>
{
	CSevenZipLibrary();
	~CSevenZipLibrary();

	STDMETHODIMP SetPath(__in_opt LPCWSTR);

	template<typename Interface>
	STDMETHODIMP CreateObject(__in REFCLSID, CComPtrBase<Interface> &);
	STDMETHODIMP CreateObject(__in REFCLSID, __in REFIID, __deref_out PVOID *);
	
	STDMETHODIMP GetFormatCount(__out PUINT32);
	STDMETHODIMP GetFormatProperty(UINT32, PROPID, __out LPPROPVARIANT);
	STDMETHODIMP GetFormatPropertyAsCLSID(UINT32, PROPID, __out CLSID &);
	STDMETHODIMP GetFormatPropertyAsString(UINT32, PROPID, __out CStringW &);

	STDMETHODIMP GetMethodCount(__out PUINT32);
	STDMETHODIMP GetMethodProperty(UINT32, PROPID, __out LPPROPVARIANT);
	STDMETHODIMP GetMethodPropertyAsCLSID(UINT32, PROPID, __out CLSID &);
	STDMETHODIMP GetMethodPropertyAsString(UINT32, PROPID, __out CStringW &);

private:

	template<typename T>
	STDMETHODIMP GetProcAddress(LPCSTR ProcName, T& Pointer);
	STDMETHODIMP EnsureLibraryLoaded();

	STDMETHODIMP GetHandlerProperty(PROPID, __out LPPROPVARIANT);
	STDMETHODIMP GetHandlerProperty2(UINT32, PROPID, __out LPPROPVARIANT);

private:

	typedef HRESULT (WINAPI * CreateObjectFunc)(__in REFCLSID, __in REFIID, __deref_out PVOID *);
	typedef HRESULT (WINAPI * GetNumberOfFunc)(__out PUINT32);
	typedef HRESULT (WINAPI * GetPropertyFunc)(PROPID, __out LPPROPVARIANT);
	typedef HRESULT (WINAPI * GetProperty2Func)(UINT32, PROPID, __out LPPROPVARIANT);

	CreateObjectFunc _CreateObjectPtr;
	
	GetNumberOfFunc  _GetNumberOfMethodsPtr;
	GetProperty2Func _GetMethodPropertyPtr;

	GetNumberOfFunc  _GetNumberOfFormatsPtr;
	GetPropertyFunc  _GetHandlerPropertyPtr;
	GetProperty2Func _GetHandlerProperty2Ptr;
	
	BOOL     _Legacy;
	HMODULE  _Library;
	CStringW _LibraryPath;

private:

	static CONST LPCWSTR LibraryName;
	static STDMETHODIMP FindLibrary(__out_ecount_z(MAX_PATH) LPWSTR);
};

typedef CComPtr<CSevenZipLibrary> CSevenZipLibraryPtr;
}

template<typename T>
STDMETHODIMP SevenZip::CSevenZipLibrary::GetProcAddress(LPCSTR ProcName, T& Pointer)
{
	HRESULT hr = EnsureLibraryLoaded();

	if (SUCCEEDED(hr) && (Pointer == NULL))
	{
		Pointer = reinterpret_cast<T>(::GetProcAddress(_Library, ProcName));

		if (Pointer == NULL)
		{
			hr = HRESULT_FROM_WIN32(::GetLastError());
		}
	}

	return hr;
}

template<typename Interface>
STDMETHODIMP SevenZip::CSevenZipLibrary::CreateObject(REFCLSID ClassID, CComPtrBase<Interface> & Result)
{
	return CreateObject(ClassID, __uuidof(Interface), reinterpret_cast<PVOID*>(&Result));
}